import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

public class Main {

	public static int totalDocumentNumber = 6413;
	private static final String cvsFileName = "VSM_Effectiveness.csv";
	public static final String corpusMethodFileName = "CorpusMethods-jEdit4.3-AfterSplitStopStem.txt"; //
	// public static final String corpusMethodFileName = "testinput.txt";
	public static final String corpusQueryFileName = "CorpusQueries-jEdit4.3-AfterSplitStopStem.txt";
	public static final String tempQueryFilePrefx = "Qry";
	public static final String workingPath = GetCurrentPath() + "jEdit4.3/"; 
	private static final String csvSeprator = "\t";

	//query& method ids
	public static LinkedList<Integer> queryIDs = new LinkedList<Integer>();
	public static LinkedList<String> methodIDs = new LinkedList<String>();

	
	// tf for all docs
	public static LinkedList<HashMap<String, Double>> tf = new LinkedList<HashMap<String, Double>>();

	// idf
	public static HashMap<String, Integer> df = new HashMap<String, Integer>();
	public static HashMap<String, Double> idf = new HashMap<String, Double>();

	// w
	public static LinkedList<HashMap<String, Double>> w = new LinkedList<HashMap<String, Double>>();

	// sim values for all docs
	// public static LinkedList<Double> simValues = new
	// LinkedList<Double>();//linked list //move to Query Class

	public static void main(String[] args) {

		// get all unique words
		calculateTfIdf();

		readQueryAndMethodIDs();
		
		calculateSimsFromQryFile(corpusQueryFileName);
		/*
		 * test the example:
		 */
		// given a query here
		/*
		 * String query = "t4 t7 t7"; Query q1 = new Query(1, query, tf, idf );
		 * if(IDebug.ENANLE) q1.PrintSimValues();
		 * q1.printInfo2File("testrun.txt");
		 */
		//
		System.out.println("Program finished successfully!");
	}

	/*
	 * move to Query Class 20130926 kaidi // public static double
	 * sim(HashMap<String, Integer> a, HashMap<String, Double> b){ // // double
	 * top = 0; // int p1 = 0; // for (Entry<String, Integer> entry :
	 * a.entrySet()) { // String aWordinA = entry.getKey(); // Integer aValue =
	 * entry.getValue(); // if(b.keySet().contains(aWordinA)){ // top = top +
	 * aValue * b.get(aWordinA); // } // p1 = p1 + aValue*aValue; // } // //
	 * //p2 // double p2 = 0; // for (Entry<String, Double> entry :
	 * b.entrySet()) { // double bValue = entry.getValue(); // p2 = p2 + bValue
	 * * bValue; // } // // //System.out.println("------" + top + " --- " + p1 +
	 * "   -----" + p2); // // return top/(Math.sqrt(p1) * Math.sqrt(p2)); // }
	 */

	public static void calculateTfIdf() {
		// File dir = new File(".");
		File fin;
		FileInputStream fis = null;
		try {
			/*
			 * 20130926Kaidimove to GetCurrentPath()
			 */
			System.out.println("working path: " + workingPath);
			String path = workingPath + corpusMethodFileName; // dir.getCanonicalPath()
																// +
																// File.separator
			fin = new File(path);
//			System.out.println("working path: " + path);
			fis = new FileInputStream(fin);
		} catch (IOException e) {
			e.printStackTrace();
		}

		BufferedReader in = new BufferedReader(new InputStreamReader(fis));

		int mthdCount = 0;
		String aLine = null;
		try {
			// read line by line
			while ((aLine = in.readLine()) != null) {
				mthdCount++;
				if (IDebug.ENANLE)
					System.out.println(aLine);
				String[] wordsInALine = aLine.split(" ");

				HashMap<String, Integer> frequency = new HashMap<String, Integer>();
				HashMap<String, Double> tfj = new HashMap<String, Double>();

				// tf
				for (String aWord : wordsInALine) {
					if (frequency.keySet().contains(aWord)) {
						frequency.put(aWord, frequency.get(aWord) + 1);
					} else {
						frequency.put(aWord, 1);
					}
				}

				Integer max = Collections.max(frequency.values());

				for (Map.Entry<String, Integer> entry : frequency.entrySet()) {
					double x = ((entry.getValue() * 1.0) / max);
					// System.out.println(x);
					tfj.put((String) entry.getKey(), x);
				}

				tf.add(tfj);

				// df
				// get unique set of words
				Set<String> wordInaLineSet = new HashSet<String>(
						Arrays.asList(wordsInALine));

				// System.out.println(mySet);
				for (String word : wordInaLineSet) {
					if (df.keySet().contains(word)) {
						df.put(word, df.get(word) + 1);
					} else {
						df.put(word, 1);
					}

				}

			}
			System.out.println("total method#: "+mthdCount);
			totalDocumentNumber = mthdCount;
		} catch (IOException e) {
			e.printStackTrace();
		}

		// System.out.println("totoal: " + uniqueWordsSet.size());

		// System.out.println("totoal: " + df.size());

		df2idf(df, idf);

		// printMap(df);

		// printMap(idf);

		try {
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void df2idf(HashMap<String, Integer> tf,
			HashMap<String, Double> idf) {
		for (Map.Entry<String, Integer> entry : tf.entrySet()) {

			// System.out.println(pairs.getKey() + " = " + pairs.getValue());

			double x = Math.log((totalDocumentNumber * 1.0)
					/ (Integer) entry.getValue());

			// System.out.println(x);
			idf.put((String) entry.getKey(), x);
		}
	}

	public static void printMap(Map mp) {
		Iterator it = mp.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry pairs = (Map.Entry) it.next();
			System.out.println(pairs.getKey() + " = " + pairs.getValue());
			// it.remove(); // avoids a ConcurrentModificationException
		}
	}

	/**
	 * get current path
	 * 
	 * @return current path
	 */
	public static String GetCurrentPath() {
		File dir = new File(".");
		String path;
		try {
			path = dir.getCanonicalPath() + File.separator;
			return path;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

	}

	public static void readQueryAndMethodIDs() {
		File qryFileIn;
		FileInputStream fis = null;
		BufferedReader in = null;
		try {

			qryFileIn = new File(workingPath + "jEdit4.3ListOfFeatureIDs.txt");
			fis = new FileInputStream(qryFileIn);
			in = new BufferedReader(new InputStreamReader(fis));

			String aLine = null;
			// read line by line
			while ((aLine = in.readLine()) != null) {
				queryIDs.add(Integer.parseInt(aLine.trim()));
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		
		try {

			qryFileIn = new File(workingPath + "CorpusMethods-jEdit4.3.mapping");
			//System.out.println(qryFileIn);
			fis = new FileInputStream(qryFileIn);
			in = new BufferedReader(new InputStreamReader(fis));

			String aLine = null;
			// read line by line
			while ((aLine = in.readLine()) != null) {
				methodIDs.add(aLine.trim());
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		
	}
	/**
	 * compute sims for every query line
	 * 
	 * @param filePath
	 */
	public static void calculateSimsFromQryFile(String filePath) {
		File qryFileIn;
		FileInputStream fis = null;
		BufferedReader in = null;
		try {

			qryFileIn = new File(workingPath + filePath);

			// System.out.println("query file path: " + filePath);

			if (qryFileIn.isFile() && qryFileIn.exists()) {
				fis = new FileInputStream(qryFileIn);
				in = new BufferedReader(new InputStreamReader(fis));

				int lineNum = 0;
				String aLine = null;
				// read line by line
				while ((aLine = in.readLine()) != null) {
					// if (IDebug.ENANLE)

					// generate one Query here
					int queryID = queryIDs.get(lineNum);
					lineNum++;
					if(IDebug.ENANLE)
						System.out.println(lineNum + " -" + aLine);
					
					Query qry = new Query(queryID, aLine, tf, idf);
					qry.calculateResult(methodIDs);
					printQueryResult2CSV(cvsFileName, qry);
					//String tmpFileName = "Q" + lineNum + ".txt";
					//qry.printInfo2File(tmpFileName);
				}
			} else
				return;
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * print result for one query to csv file
	 * @param filePath
	 * @param qry
	 */
	public static void printQueryResult2CSV(String filePath, Query qry)
	{
		
		File afile = new File(filePath);
		boolean afileExist = afile.exists();
		FileWriter fstream = null;
		try 
		{
			fstream = new FileWriter(filePath, true);
			BufferedWriter out = new BufferedWriter(fstream);
			if(!afileExist)
			{
				System.out.println("Generating " + cvsFileName);
				out.write("featureID"+csvSeprator
					+"GoldSetMethodID Postion"+csvSeprator
					+"GoldSetMethodID"+csvSeprator
					+"VSM GoldSetMethodID Rank-All Ranks"+csvSeprator
					+"VSM GoldSetMethodID Rank-Best Rank");
				out.newLine();
			}

			if(IDebug.ENANLE)
				System.out.println("gold set: ");
			boolean first = true;
			
			for(GoldSetMethod aMethod: qry.goldSetMethodList)
			{
	//			LinkedList<String> methodIDs = Main.methodIDs;
//				int lineNumberOfMethod = methodIDs.indexOf(aMethodID);//careful about offset
//				if(lineNumberOfMethod >=0)
//					{lineNumberOfMethod++;}
				String aMethodID = aMethod.MethodID;
				int lineNumberOfMethod = aMethod.LineNumber;
				int rank = aMethod.Rank;
				if(IDebug.ENANLE)
					System.out.println(lineNumberOfMethod + " " + aMethodID);
				
				if(first){
//					bestRank = index;
					out.write(qry.queryID+csvSeprator+
							lineNumberOfMethod + csvSeprator 
							+ "\""+aMethodID+"\"" + csvSeprator);
//					first = false;
				}else{
					out.write(csvSeprator+
							+lineNumberOfMethod + csvSeprator 
							+"\""+aMethodID+"\"" + csvSeprator);
				}
				
//				// compute the ranks for the method
				//output the method rank
				if(lineNumberOfMethod > 0){
//					Double d = qry.simValLinkList.get(lineNumberOfMethod-1);
//					int index = Arrays.asList(array).indexOf(d) +1;//careful about offset
					if(IDebug.ENANLE)
						System.out.println("rank : " + rank);
					out.write(rank+csvSeprator);
					
				}else{
						out.write(csvSeprator);
//						out.newLine();
				}
				
				if(first)
				{
					out.write(qry.goldSetBestRank+csvSeprator);
//					if(IDebug.ENANLE)
//						System.out.println(qry.goldSetBestRank);
					first = false;
				}
				System.out.println(aMethodID+" is written into "+  cvsFileName);
				out.newLine();
		}
		out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
